#ifndef CSGNODE_H
#define CSGNODE_H

#include "primitive.h"
#include "material.h"
#include "ray.h"

#include "glm/gtx/transform.hpp"

#include <QSharedPointer>
#include <vector>
#include <list>

// Some predefinitions
class CSGNode;
class CSGPrimitive;
class CSGBooleanOperation;
class CSGGeometricOperation;

// Defines a simple ray intersection interval i.e. entry point, exit point
class CSGIntersection {
public:
	// Constructor, takes:
	// t1, t2 ray collision parameters for entry and exit points respectively
	// normal1, normal2 points normal for entry and exit points respectively
	// primitive1, primitive2 CSGPrimitives for entry and exit points respectively
	CSGIntersection( float t1, float t2,  glm::vec4 normal1, glm::vec4 normal2,
			CSGPrimitive *primitive1, CSGPrimitive *primitive2
			);
	// Returns true if there is a overlapping between this interval and the parameter, false otherwise
	bool do_overlap(CSGIntersection &intersection);
// TODO: Change this to private? and implement Getters/Setters
	// Attributes for entry and exit point
	float t[2];			// the t parameter defines the point intercepted by the ray
	glm::vec4 normal[2];		// this point normal in the resulting model
	CSGPrimitive *primitive[2];		// Primitive that was intercepted - this should be useful for obtaining material properties
};

enum NodeType { PRIMITIVE_NODE, BOOLEAN_NODE, TRANSFORMATION_NODE };

// CSGTree construction block: represents a CSGTree node that can be either a CSGOperation or a CSGPrimitive
class CSGNode
{
public:
	// This node's parent(optional)
	CSGNode( CSGNode * parent = NULL );       
	// Returns a list of intervals (in, out) parameterized t values
	virtual const std::list<CSGIntersection> & set_membership(const Ray &ray) = 0;
	// Add a new son and consequently set this node as the son's parent
	void add_son(QSharedPointer<CSGNode> node);

	// Propagate an event update matrixes accums/ops -- should be called only once per operation execution on
	// some primitive/node e.g. a translation in a object, call this before traversing the tree
	virtual void update_matrix();
	const glm::mat4 & get_accumulated_inverse_matrix();

	// Getter/Setter to obtain/set this node's parent
	QSharedPointer<CSGNode> get_parent();
        void set_parent(CSGNode *ptr);
        int m_id;
        NodeType m_node_type;
        bool m_top_node;
        void set_top(bool top_node);
        bool m_visible;
protected:
	// Sons node list: at first, only 2 sons?
	std::vector<QSharedPointer<CSGNode> > m_sons;
	// This node's intersections - FOR THE LAST TIME set_membership was called
	std::list<CSGIntersection> m_intersections;
	// This node parent
	QSharedPointer<CSGNode> m_parent;
	// The tree so far accumulated matrix inverse til this node -- Should be calculated only one time per-frame
	glm::mat4 m_inv_accum_operation;
};

// CSGNode representing a primitive

class CSGPrimitive : public CSGNode
{
public:
        CSGPrimitive( PrimitiveType type, NodeType node_type = PRIMITIVE_NODE, int id = -1 );	// Accordingly to the given type, we will instantiate an appropriate Primitive       
        // In this case, it is just call Primitive->get_intersection(ray) -- or something like that
	virtual const std::list<CSGIntersection> & set_membership(const Ray &ray);
	//private:
	// TODO: change to private and implement Getters/Setters
	PrimitiveType m_type;
        Primitive *m_primitive;
        Material *m_material;
	// TODO: node attributes for primitives like material etc
//	glm::vec3 spec, ambient blah
};

// CSGNode implemented Boolean/Geometric operations
typedef enum {
	// Boolean Operations
	UNION, INTERSECTION, DIFFERENCE,
	// Geometric Operations
	TRANSLATION, ROTATION, SCALE } OperationType;

// CSGNode representing a boolean operation
class CSGBooleanOperation : public CSGNode
{
public:
        CSGBooleanOperation( OperationType type, NodeType node_type = BOOLEAN_NODE, int id = -1 );
	virtual const std::list<CSGIntersection> & set_membership(const Ray &ray);
	void perform_union( const std::list<CSGIntersection> & first, const std::list<CSGIntersection> & second );
	void perform_intersection( const std::list<CSGIntersection> & first, const std::list<CSGIntersection> & second );
	void perform_difference( const std::list<CSGIntersection> & first, const std::list<CSGIntersection> & second );
private:
	OperationType m_type;
};


// CSGNode representing a geometric operation
class CSGGeometricOperation : public CSGNode
{
public:
        CSGGeometricOperation( OperationType type, NodeType node_type = TRANSFORMATION_NODE, int id = -1 ) ;
	virtual const std::list<CSGIntersection> & set_membership(const Ray &ray);
	// Performs an update in m_inv_matrix concatenating it this way
	// new_transform_matrix_inverse * m_inv_matrix
	void perform_translation( glm::vec3 & );
	void perform_rotation( glm::vec3 & rot_vector, float angle );
	void perform_scale( glm::vec3 & sca_factor );

	// Will update accum inv matrix to m_inv_matrix*(this node parent accum inverse)
	void update_matrix();	// polymorphism over update_matrix in CSGNode
private:
	glm::mat4 m_inv_matrix;		// Inverse of this node operations

	//   glm::vec3 rotation_vector;
	//   float rotation_angle;
	//   glm::vec3 scale_factor;
	//   glm::vec4 translation_vector;
	OperationType m_type;
};
#endif // CSGNODE_H
